﻿using System;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Communication.Net.Ftp
{
    /// <summary>
    /// 接收数据并且在服务器站点保存为文件
    /// Accept the data and to store the data as a file at the server site
    /// </summary>
    internal class FtpSTORCommand : FtpCommandAbs
    {
        /***********************************************************************************************************
        * [STOR]（Store,保存）命令
        * 说明：让服务器接收一个来自数据连接的文件。
        * 
        * 用法：STOR<FileName><CRLF>
        * 参数：FileName是服务器站点上一个完全合格的路径和文件名。
        * 例子：
        * SendData "STOR newfile.zip" + “/r/n
        * 
        * 注释：Store命令让服务器接收通过数据连接传输而来的数据，并把数据存为务器站点上的文件。如果在路径名中指定的文件已经在服务站点上存在，则此文件的内容将被传输过来的数据所替代。如果在路径名中指定的文件尚未存在，将创建一个新文件。
        * 返回值如下（值体表示成功）：
        * 110  重新启动标记应答
        * 
        * 在此情况下，文本是精确的，而且不会留给特殊的实现处理，它必须读取：
        * MARK yyyy=mmmm
        * 
        * 其中yyyy是用户进程的数据流标记，mmmm是服务器上的相应相标记（请注意标记和“=”之间的空格）。
        * 125  数据连接已打开，传输启动。
        * 150  文件状态一切正常，准备打开数据连接。
        * 226  关闭数据连接，请求的文件操作已成功。
        * 250  请求的文件操作正常进行，已完成。
        * 421  服务不可用，关闭控制连接。
        * 
        * 如果某项服务获知自己即将关闭，会向所有命令做出这个应答。
        * 425  无法打开数据连接。
        * 426  连接关闭，传输中止。
        * 450  请求的文件操作无法执行，文件不可用（例如文件正忙）
        * 451  请求的操作被中止，处理中发生本地错误。
        * 452  请求的操作无法执行，系统的存储空间不足。
        * 500  语法错误，无法识别命令。这其中包括命令行过长之类的错误。
        * 501  参数或变元中有语法错误。
        * 504  命令中还没有实现该参数。
        * 530  无法登录。
        * 532  文件的存储需要帐号。
        * 550  请求的操作无法执行，文件不可用（例如找不到文件，无访问权）。
        * 551  请求的操作被中止，未知的页类型。
        * 552  请求的文件操作被子中止，超过了分配的存储单元（对当前目录或数据集而言）。
        * 553  请求的操作无法执行，不允许的文件名。
        * 
        ************************************************************************************************************/



        /***********************************************************************************************************
        * [STOU]（Store Unique，存为唯一文件）命令
        * 说明：让服务器准备接收一个文件，并指示服务器把这个文件用唯一的名称保存到目的目录中。
        * 
        * 用法：STOU<CRLF>
        * 参数：无。
        * 例子：
        * SendData "STOU"&vbCrlf
        * 
        * 注释：Store Unique命令和STOR所做的工作是基本一样的，只是前者在当前目录下生成的文件是以一个该目录中唯一的名称来创建的。250 Transfer Started响应中必须包括这个生成的名称。
        * 返回值如下（粗体表示成功）：
        * 110  重新启动标记应答
        * 
        * 在此情况下，文本是精确的，而且不会留给特殊的实现处理，它必须读取：
        * MARKyyyy=mmmm
        * 其中yyyy是用户进程的数据流标记，mmmm是服务器上的相应标记（请注意标记和“=”之间的空格）。
        * 125  数据连接已打开，传输启动。
        * 150  文件状态正常，准备打开数据连接。
        * 226  关闭数据连接，请求的文件操作已成功。
        * 250  请求的文件操作没问题，已完成（该行中包括一个唯一的文件名）。
        * 421  服务不可用，关闭控制连接。
        * 
        * 如果某项服务获知自己即将关闭，会向所有命令做出这个应答。
        * 425  无法打开数据连接。
        * 426  连接关闭，传输中止。
        * 450  请求的文件操作无法执行，文件不可用（例如文件正忙）。
        * 451  请求的操作被中止，处理中发生本地错误。
        * 452  请求的操作无法执行，系统的存储空间不足。
        * 500  语法错误，无法识别命令。这其中包括命令行过长之类的错误。
        * 501  参数或变元中有语法错误。
        * 504  命令中还没有实现该参数。
        * 530  无法登录。
        * 532  文件的存储需要帐号。
        * 550  请求的操作无法执行，文件不可（例如找不到文件，无访问权）。
        * 551  请求的操作作被中止，未知的页类型。
        * 552  请求的文件操作被中止，超过了分配的存储单元（对当前目录或数据集而言）。
        * 553请求的操作无法执行，存在不允许的文件名。
        * 
        ************************************************************************************************************/


        public FtpSTORCommand()
            : base(true)
        {

        }

        protected override string[] GetNames()
        {
            return new string[] { FtpCommands.STOR, FtpCommands.SEND, FtpCommands.PUT, FtpCommands.STOU };
        }

        protected override bool CheckLastCommand(FtpSession session)
        {
            if (session.LastCommandIsTranferCommand())
            {
                return true;
            }

            return false;
        }

        protected override IFtpResponse PrimitiveExcute(FtpSession session, string command, string commandLine)
        {
            //Request: STOR AnyDesk.exe
            //Response: 125 Data connection already open; Transfer starting.
            //Response: 226 Transfer complete.
            //Response: 550 Access is denied. 

            try
            {
                if (!session.AllowWrite())
                {
                    return new FtpCodeResponse(FtpStatusCodes.Code550, "Access is denied. ");
                }

                string fileName;
                if (string.Equals(command, FtpCommands.STOU))
                {
                    fileName = Guid.NewGuid().ToString("N") + ".dat";
                }
                else
                {
                    fileName = commandLine;
                    if (string.IsNullOrEmpty(fileName))
                    {
                        return new FtpCodeResponse(FtpStatusCodes.Code500, "The file name parameter is required");
                    }
                }

                string filePath = session.GetLocalPath(fileName);
                DirectoryInfoEx.CheckFilePathDirectory(filePath);
                using (var tempDataTranfer = session.GetDataTranferObject())
                {
                    session.AddDataTranfer(tempDataTranfer);

                    IFtpResponse response;
                    var result = tempDataTranfer.CreateReceiveTcpClient(filePath, false, out response);
                    if (result)
                    {
                        session.Send(response);

                        response = tempDataTranfer.ReceiveData();
                    }

                    return response;
                }
            }
            catch (Exception ex)
            {
                return new FtpCodeResponse(FtpStatusCodes.Code451, ex.Message);
            }
        }
    }

}
